package com.platform.aspect;

import com.platform.comm.enums.YnEnum;
import com.platform.comm.util.CommUtil;
import com.platform.comm.util.HttpUtil;
import com.platform.core.security.auth.AuthenticationFacade;
import com.platform.core.service.SysLogService;
import com.platform.dto.LogQo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * 日志切面：作用于系统角色(sysRoleController)，用户(sysUserController)，授权(sysRoleFunctionController)
 * 2021/02/25:记录用户登录信息(ProfileController)
 *
 * @author qy created on 2020/1/6
 **/
@Aspect
@Slf4j
@Component
public class LogAspect {
    private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    private final SysLogService sysLogService;

    public LogAspect(SysLogService sysLogService) {
        this.sysLogService = sysLogService;
    }

    /**
     * 请求执行失败，日志记录执行的失败信息
     *
     * @param join   切面
     * @param webLog 日志注解
     * @param ex     异常
     */
    @AfterThrowing(throwing = "ex", pointcut = "execution(* com.platform..*.*(*)) && @annotation(webLog)")
    public void throwInfoLog(JoinPoint join, WebLog webLog, Exception ex) {
        log.info("Starting save failLogInfo...");
        LogQo entity = new LogQo();
        entity.setId(CommUtil.uuid());
        entity.setOperateName(webLog.type().getText());
        entity.setModuleName(webLog.moduleName());
        ServletRequestAttributes attriButes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attriButes != null) {
            HttpServletRequest request = attriButes.getRequest();
            entity.setOperateType(request.getMethod());
            entity.setRequestUrl(request.getRequestURL().toString());
        }
        if (StringUtils.equals(webLog.moduleName(),"采报大厅")){
            handleGatherOperate(entity,join,webLog);
        }
        entity.setUserId(AuthenticationFacade.getUserId() ==null?"":AuthenticationFacade.getUserId());
        entity.setLoginName(AuthenticationFacade.getUserName() == null? "":AuthenticationFacade.getUserName());
        entity.setOperateDate(LocalDateTime.now());
        entity.setRemoteAddress(HttpUtil.getIpAddress());
        entity.setActionResult(getActionResult(join, webLog) + ":失败(" + ex.toString() + ")");
        entity.setSuccess(YnEnum.N);
//        sysLogService.create(entity);
        log.info("通用日志框架信息(FAIL):"+entity.getModuleName()+"-"+entity.getActionResult());
    }

    /**
     * 请求正常执行，记录日志
     *
     * @param join   切面
     * @param obj    请求方法的返回值
     * @param webLog 日志注解
     */
    @AfterReturning(returning = "obj", pointcut = "execution(* com.platform..*.*(*)) && @annotation(webLog)")
    public void resultLog(JoinPoint join, WebLog webLog, Object obj) {
        log.info("Starting save successLogInfo...");
        LogQo entity = new LogQo();
        entity.setId(CommUtil.uuid());
        entity.setOperateName(webLog.type().getText());
        if (StringUtils.equals(webLog.moduleName(),"采报大厅")){
            handleGatherOperate(entity,join,webLog);
        }
        entity.setModuleName(webLog.moduleName());
        ServletRequestAttributes attriButes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attriButes != null) {
            HttpServletRequest request = attriButes.getRequest();
            entity.setOperateType(request.getMethod());
        }
        entity.setRequestUrl(join.getStaticPart().getSignature().getDeclaringTypeName());
        entity.setUserId(AuthenticationFacade.getUserId() ==null?"":AuthenticationFacade.getUserId());
        entity.setLoginName(AuthenticationFacade.getUserName() == null? "":AuthenticationFacade.getUserName());
        entity.setOperateDate(LocalDateTime.now());
        entity.setRemoteAddress(HttpUtil.getIpAddress());
        entity.setActionResult(getActionResult(join, webLog) + "成功");
        entity.setSuccess(YnEnum.Y);
        sysLogService.create(entity);
        log.info("通用日志框架信息(SUCCESS):"+entity.getModuleName()+"-"+entity.getActionResult());
    }

    private void handleGatherOperate(LogQo entity,JoinPoint join, WebLog webLog){
        Object[] args = join.getArgs();
        LogQo logQo = (LogQo) args[0];
        String type = logQo.getType();
        entity.setType(type);
    }
    private String getActionResult(JoinPoint join, WebLog webLog) {
        Object[] args = join.getArgs();
        StandardEvaluationContext context = new StandardEvaluationContext();
        Class<?> rootClz = webLog.root();
        if (rootClz != String.class) {
            Optional<Object> optional = Stream.of(args).filter(rootClz::isInstance)
                    .findFirst();
            optional.ifPresent(context::setRootObject);
        } else {
            Method method = ((MethodSignature) join.getSignature()).getMethod();
            String[] params = discoverer.getParameterNames(method);
            String[] logParams = webLog.params();
            for (String logParam : logParams) {
                int idx = ArrayUtils.indexOf(params, logParam);
                assert params != null;
                context.setVariable(params[idx], args[idx]);
            }
        }
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(webLog.theme(), new TemplateParserContext());
        return expression.getValue(context, String.class);
    }
}
